﻿#include "Utils.h"

FrameWriter Utils::initFrameWriter(int width, int height) {
    FrameWriter writer{};
    glGenFramebuffers(1, &writer.fbo);
    glGenTextures(1, &writer.texture);

    glBindTexture(GL_TEXTURE_2D, writer.texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, writer.fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, writer.texture, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glGenBuffers(1, &writer.pbo);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, writer.pbo);
    glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 3,  NULL, GL_STREAM_READ);

    writer.shaderProgram = Utils::activateShader("./GLSL/RGBAtoRGB_Vertex.glsl", "./GLSL/RGBAtoRGB_Fragment.glsl");

    writer.width = width;
    writer.height = height;

    screenQuad.initialize();
    return writer;
}

void Utils::clearFrame(GLint fbo) {
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

uint8_t* Utils::getFrame(FrameWriter writer, GLuint fbo, GLuint texture) {
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        std::cerr << "FBO 不完整，错误码：" << status << std::endl;
        return nullptr;
    }

    Utils::clearFrame(writer.fbo);

    // 绑定目标FBO
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glViewport(0, 0, writer.width, writer.height);

    // 使用着色器程序并绑定原FBO的RGBA纹理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(glGetUniformLocation(writer.shaderProgram, "sourceTexture"), 0);

    // 渲染全屏四边形（触发片段着色器）
    glBindVertexArray(screenQuad.VAO);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);

    // 1. 设置像素存储对齐参数
    GLint original_alignment;
    glGetIntegerv(GL_PACK_ALIGNMENT, &original_alignment);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);  // 强制1字节对齐

    // 计算所需内存大小
    size_t total_size = writer.width * writer.height * 3;

    // 分配 PBO 缓冲区（不直接分配 CPU 内存）
    glBindBuffer(GL_PIXEL_PACK_BUFFER, writer.pbo);
    glBufferData(GL_PIXEL_PACK_BUFFER, total_size, nullptr, GL_STREAM_READ);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    // 2. 从 FBO 异步读取到 PBO（最后一个参数为 0，表示 PBO 的偏移量）
    glReadPixels(0, 0, writer.width, writer.height, GL_RGB, GL_UNSIGNED_BYTE, 0);

    // 3. 映射 PBO 到 CPU 内存
    glBindBuffer(GL_PIXEL_PACK_BUFFER, writer.pbo);
    uint8_t* pixels = static_cast<uint8_t*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
    if (!pixels) {
        std::cerr << "Failed to map PBO!" << std::endl;
        return nullptr;
    }

    // 4. 复制数据到新分配的数组（避免返回映射指针）
    uint8_t* result = new uint8_t[total_size];
    memcpy(result, pixels, total_size);

    // 5. 清理资源
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

    // 恢复原始像素对齐设置
    glPixelStorei(GL_PACK_ALIGNMENT, original_alignment);

    // 解绑FBO
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    return result;
}